package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"syscall"
	"time"
	
	"gollect/monitor/db"
)

// Global verbose flag
var verbose bool

// initDB initializes the global database connection
func initDB() error {
	var err error
	database, err = db.New("data/monitor.db")
	if err != nil {
		return fmt.Errorf("failed to initialize database: %w", err)
	}
	return nil
}

func main() {
	// Check if we have subcommands
	if len(os.Args) > 1 {
		subcommand := os.Args[1]
		
		switch subcommand {
		case "deploy-node":
			handleDeployNode()
			return
		case "deploy-all":
			handleDeployAll()
			return
		case "bootstrap-node":
			handleBootstrapNode()
			return
		case "bootstrap-all":
			handleBootstrapAll()
			return
		case "stop-node":
			handleStopNode()
			return
		case "restart-node":
			handleRestartNode()
			return
		case "status-node":
			handleStatusNode()
			return
		case "logs-node":
			handleLogsNode()
			return
		case "list-nodes":
			handleListNodes()
			return
		case "test-connection":
			handleTestConnection()
			return
		case "migrate":
			handleMigrate()
			return
		case "server":
			handleServer()
			return
		case "help":
			showHelp()
			return
		}
	}

	// Default behavior - run the monitor server
	handleServer()
}

func showHelp() {
	fmt.Println(`Holter Monitor - Remote Node Management System

Usage: go run . [COMMAND] [ARGS]

Commands:
    server                    Start the monitoring server (default)
    deploy-node NODE_ID PORT  Deploy trode to a specific node
    deploy-all START_PORT     Deploy trodes to all nodes
    bootstrap-node NODE_ID    Bootstrap a specific node
    bootstrap-all            Bootstrap all nodes
    stop-node NODE_ID PORT   Stop trode on a specific node
    restart-node NODE_ID PORT Restart trode on a specific node
    status-node NODE_ID PORT Check trode status on a specific node
    logs-node NODE_ID PORT LINES Get trode logs from a specific node
    list-nodes               List all nodes in database
    test-connection NODE_ID  Test SSH connection to a node
    migrate                  Run database migrations
    help                     Show this help message

Examples:
    go run . deploy-node 1 8080
    go run . bootstrap-all
    go run . status-node 1 8080
    go run . list-nodes
`)
}

func handleDeployNode() {
	if len(os.Args) < 4 {
		fmt.Println("Usage: go run . deploy-node NODE_ID PORT")
		os.Exit(1)
	}

	nodeID, err := strconv.Atoi(os.Args[2])
	if err != nil {
		fmt.Printf("Invalid node ID: %s\n", os.Args[2])
		os.Exit(1)
	}

	port, err := strconv.Atoi(os.Args[3])
	if err != nil {
		fmt.Printf("Invalid port: %s\n", os.Args[3])
		os.Exit(1)
	}

	// Initialize database
	if err := initDB(); err != nil {
		fmt.Printf("Failed to initialize database: %v\n", err)
		os.Exit(1)
	}

	result, err := DeployTrodeToNode(nodeID, port)
	if err != nil {
		fmt.Printf("Deployment error: %v\n", err)
		os.Exit(1)
	}

	if result.Success {
		fmt.Printf("✅ Successfully deployed trode to node %d on port %d\n", nodeID, port)
		fmt.Printf("Service name: %s\n", result.ServiceName)
	} else {
		fmt.Printf("❌ Deployment failed: %s\n", result.Error)
		fmt.Printf("Failed at step: %s\n", result.Step)
		os.Exit(1)
	}
}

func handleDeployAll() {
	startPort := 8080
	if len(os.Args) > 2 {
		if port, err := strconv.Atoi(os.Args[2]); err == nil {
			startPort = port
		}
	}

	// Initialize database
	if err := initDB(); err != nil {
		fmt.Printf("Failed to initialize database: %v\n", err)
		os.Exit(1)
	}

	results, err := DeployTrodeToAllNodes(startPort)
	if err != nil {
		fmt.Printf("Deployment error: %v\n", err)
		os.Exit(1)
	}

	successCount := 0
	for i, result := range results {
		if result.Success {
			fmt.Printf("✅ Node %d: Successfully deployed on port %d\n", i+1, result.TrodePort)
			successCount++
		} else {
			fmt.Printf("❌ Node %d: Failed - %s\n", i+1, result.Error)
		}
	}

	fmt.Printf("\nDeployment summary: %d/%d nodes deployed successfully\n", successCount, len(results))
	if successCount < len(results) {
		os.Exit(1)
	}
}

func handleBootstrapNode() {
	if len(os.Args) < 3 {
		fmt.Println("Usage: go run . bootstrap-node NODE_ID")
		os.Exit(1)
	}

	nodeID, err := strconv.Atoi(os.Args[2])
	if err != nil {
		fmt.Printf("Invalid node ID: %s\n", os.Args[2])
		os.Exit(1)
	}

	// Initialize database
	if err := initDB(); err != nil {
		fmt.Printf("Failed to initialize database: %v\n", err)
		os.Exit(1)
	}

	node, err := database.GetNode(nodeID)
	if err != nil {
		fmt.Printf("Failed to get node: %v\n", err)
		os.Exit(1)
	}

	result, err := BootstrapNode(node)
	if err != nil {
		fmt.Printf("Bootstrap error: %v\n", err)
		os.Exit(1)
	}

	if result.Success {
		fmt.Printf("✅ Successfully bootstrapped node %d (%s)\n", nodeID, node.Name)
		fmt.Printf("SSH key path: %s\n", result.KeyPath)
	} else {
		fmt.Printf("❌ Bootstrap failed: %s\n", result.Error)
		os.Exit(1)
	}
}

func handleBootstrapAll() {
	// Initialize database
	if err := initDB(); err != nil {
		fmt.Printf("Failed to initialize database: %v\n", err)
		os.Exit(1)
	}

	nodes, err := database.GetAllNodes()
	if err != nil {
		fmt.Printf("Failed to get nodes: %v\n", err)
		os.Exit(1)
	}

	successCount := 0
	for _, node := range nodes {
		fmt.Printf("Bootstrapping node %d (%s)...\n", node.ID, node.Name)
		
		result, err := BootstrapNode(node)
		if err != nil {
			fmt.Printf("❌ Node %d: Bootstrap error - %v\n", node.ID, err)
			continue
		}

		if result.Success {
			fmt.Printf("✅ Node %d: Successfully bootstrapped\n", node.ID)
			successCount++
		} else {
			fmt.Printf("❌ Node %d: Bootstrap failed - %s\n", node.ID, result.Error)
		}
	}

	fmt.Printf("\nBootstrap summary: %d/%d nodes bootstrapped successfully\n", successCount, len(nodes))
	if successCount < len(nodes) {
		os.Exit(1)
	}
}

func handleStopNode() {
	if len(os.Args) < 4 {
		fmt.Println("Usage: go run . stop-node NODE_ID PORT")
		os.Exit(1)
	}

	nodeID, err := strconv.Atoi(os.Args[2])
	if err != nil {
		fmt.Printf("Invalid node ID: %s\n", os.Args[2])
		os.Exit(1)
	}

	port, err := strconv.Atoi(os.Args[3])
	if err != nil {
		fmt.Printf("Invalid port: %s\n", os.Args[3])
		os.Exit(1)
	}

	// Initialize database
	if err := initDB(); err != nil {
		fmt.Printf("Failed to initialize database: %v\n", err)
		os.Exit(1)
	}

	if err := StopTrodeOnNode(nodeID, port); err != nil {
		fmt.Printf("Failed to stop trode: %v\n", err)
		os.Exit(1)
	}

	fmt.Printf("✅ Successfully stopped trode on node %d\n", nodeID)
}

func handleRestartNode() {
	if len(os.Args) < 4 {
		fmt.Println("Usage: go run . restart-node NODE_ID PORT")
		os.Exit(1)
	}

	nodeID, err := strconv.Atoi(os.Args[2])
	if err != nil {
		fmt.Printf("Invalid node ID: %s\n", os.Args[2])
		os.Exit(1)
	}

	port, err := strconv.Atoi(os.Args[3])
	if err != nil {
		fmt.Printf("Invalid port: %s\n", os.Args[3])
		os.Exit(1)
	}

	// Initialize database
	if err := initDB(); err != nil {
		fmt.Printf("Failed to initialize database: %v\n", err)
		os.Exit(1)
	}

	if err := RestartTrodeOnNode(nodeID, port); err != nil {
		fmt.Printf("Failed to restart trode: %v\n", err)
		os.Exit(1)
	}

	fmt.Printf("✅ Successfully restarted trode on node %d\n", nodeID)
}

func handleStatusNode() {
	if len(os.Args) < 4 {
		fmt.Println("Usage: go run . status-node NODE_ID PORT")
		os.Exit(1)
	}

	nodeID, err := strconv.Atoi(os.Args[2])
	if err != nil {
		fmt.Printf("Invalid node ID: %s\n", os.Args[2])
		os.Exit(1)
	}

	port, err := strconv.Atoi(os.Args[3])
	if err != nil {
		fmt.Printf("Invalid port: %s\n", os.Args[3])
		os.Exit(1)
	}

	// Initialize database
	if err := initDB(); err != nil {
		fmt.Printf("Failed to initialize database: %v\n", err)
		os.Exit(1)
	}

	running, err := CheckTrodeStatusOnNode(nodeID, port)
	if err != nil {
		fmt.Printf("Failed to check status: %v\n", err)
		os.Exit(1)
	}

	if running {
		fmt.Printf("✅ Trode is running on node %d (port %d)\n", nodeID, port)
	} else {
		fmt.Printf("❌ Trode is not running on node %d (port %d)\n", nodeID, port)
		os.Exit(1)
	}
}

func handleLogsNode() {
	if len(os.Args) < 4 {
		fmt.Println("Usage: go run . logs-node NODE_ID PORT [LINES]")
		os.Exit(1)
	}

	nodeID, err := strconv.Atoi(os.Args[2])
	if err != nil {
		fmt.Printf("Invalid node ID: %s\n", os.Args[2])
		os.Exit(1)
	}

	port, err := strconv.Atoi(os.Args[3])
	if err != nil {
		fmt.Printf("Invalid port: %s\n", os.Args[3])
		os.Exit(1)
	}

	lines := 50
	if len(os.Args) > 4 {
		if l, err := strconv.Atoi(os.Args[4]); err == nil {
			lines = l
		}
	}

	// Initialize database
	if err := initDB(); err != nil {
		fmt.Printf("Failed to initialize database: %v\n", err)
		os.Exit(1)
	}

	logs, err := GetTrodeLogsFromNode(nodeID, port, lines)
	if err != nil {
		fmt.Printf("Failed to get logs: %v\n", err)
		os.Exit(1)
	}

	fmt.Printf("=== Trode logs for node %d (port %d) ===\n", nodeID, port)
	fmt.Println(logs)
}

func handleListNodes() {
	// Initialize database
	if err := initDB(); err != nil {
		fmt.Printf("Failed to initialize database: %v\n", err)
		os.Exit(1)
	}

	nodes, err := database.GetAllNodes()
	if err != nil {
		fmt.Printf("Failed to get nodes: %v\n", err)
		os.Exit(1)
	}

	if len(nodes) == 0 {
		fmt.Println("No nodes found in database")
		return
	}

	fmt.Printf("%-4s %-20s %-15s %-8s %-10s %-12s %-8s %-12s\n", "ID", "Name", "Address", "SSH Port", "User", "Bootstraped", "Trode Port", "Deployed")
	fmt.Println(strings.Repeat("-", 100))
	
	for _, node := range nodes {
		bootstrapStatus := "❌ No"
		if node.Bootstraped {
			bootstrapStatus = "✅ Yes"
		}
		
		trodePort := "-"
		if node.TrodePort > 0 {
			trodePort = fmt.Sprintf("%d", node.TrodePort)
		}
		
		deployedStatus := "❌ No"
		if node.TrodeDeployed {
			deployedStatus = "✅ Yes"
		}
		
		fmt.Printf("%-4d %-20s %-15s %-8d %-10s %-12s %-8s %-12s\n", 
			node.ID, node.Name, node.Address, node.SSHPort, node.SSHUser, bootstrapStatus, trodePort, deployedStatus)
	}
}

func handleTestConnection() {
	if len(os.Args) < 3 {
		fmt.Println("Usage: go run . test-connection NODE_ID")
		os.Exit(1)
	}

	nodeID, err := strconv.Atoi(os.Args[2])
	if err != nil {
		fmt.Printf("Invalid node ID: %s\n", os.Args[2])
		os.Exit(1)
	}

	// Initialize database
	if err := initDB(); err != nil {
		fmt.Printf("Failed to initialize database: %v\n", err)
		os.Exit(1)
	}

	node, err := database.GetNode(nodeID)
	if err != nil {
		fmt.Printf("Failed to get node: %v\n", err)
		os.Exit(1)
	}

	result, err := database.ValidateSSHCredentials(node)
	if err != nil {
		fmt.Printf("Failed to validate SSH credentials: %v\n", err)
		os.Exit(1)
	}

	if result.Valid {
		fmt.Printf("✅ SSH connection successful to %s\n", node.Address)
		if result.HasSudo {
			fmt.Printf("✅ Sudo access confirmed\n")
		} else {
			fmt.Printf("⚠️  No sudo access (password required)\n")
		}
	} else {
		fmt.Printf("❌ SSH connection failed: %s\n", result.Error)
		os.Exit(1)
	}
}

func handleMigrate() {
	if err := initDB(); err != nil {
		fmt.Printf("Failed to initialize database: %v\n", err)
		os.Exit(1)
	}

	fmt.Println("✅ Database migration completed successfully")
}

func handleServer() {
	// Parse command line flags for server mode
	addresses := flag.String("addresses", "localhost:50051", "Comma-separated list of trode addresses")
	interval := flag.Duration("interval", 1*time.Second, "Polling interval")
	webPort := flag.Int("web-port", 8080, "Web server port")
	dbPath := flag.String("db", "holter.db", "Path to SQLite database file")
	verboseFlag := flag.Bool("verbose", false, "Enable verbose logging")
	flag.Parse()

	// Set global verbose flag
	verbose = *verboseFlag

	// Print configuration
	fmt.Printf("Trode addresses: %s\n", *addresses)
	fmt.Printf("Polling interval: %v\n", *interval)
	fmt.Printf("Web dashboard port: %d\n", *webPort)
	fmt.Printf("Database path: %s\n", *dbPath)
	fmt.Printf("Verbose logging: %v\n", verbose)

	// Create web monitor
	monitor, err := NewWebMonitor(*webPort, *interval)
	if err != nil {
		log.Fatalf("Failed to create web monitor: %v", err)
	}

	// Add trodes
	for _, addr := range strings.Split(*addresses, ",") {
		if err := monitor.AddTrode(addr, *interval); err != nil {
			LogError("Failed to add trode %s: %v", addr, err)
		}
	}

	// Start monitor
	if err := monitor.Start(); err != nil {
		log.Fatalf("Failed to start monitor: %v", err)
	}

	// Wait for interrupt signal
	stop := make(chan os.Signal, 1)
	signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
	<-stop

	// Shutdown monitor
	if err := monitor.Shutdown(); err != nil {
		LogError("Error during shutdown: %v", err)
	}
}
